#include "oled.h"
#include "oled_font.h"

// 向oled写一个字节的数据
void oled_i2c_write_byte(uint8_t ctl_flag, uint8_t byte)
{
    while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY))
        ;
    i2c_start_on_bus(I2C0);
    while (!i2c_flag_get(I2C0, I2C_FLAG_SBSEND))
        ;
    i2c_master_addressing(I2C0, OLED_I2C_ADDR, I2C_TRANSMITTER);
    while (!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND))
        ;
    i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
    while (!i2c_flag_get(I2C0, I2C_FLAG_TBE))
        ;
    i2c_data_transmit(I2C0, ctl_flag);
    while (!i2c_flag_get(I2C0, I2C_FLAG_BTC))
        ;
    i2c_data_transmit(I2C0, byte);
    while (!i2c_flag_get(I2C0, I2C_FLAG_BTC))
        ;
    i2c_stop_on_bus(I2C0);
    while (I2C_CTL0(I2C0) & 0x0200)
        ;
}
// 写的数据为指令
void oled_write_command(uint8_t cmd)
{
    oled_i2c_write_byte(OLED_CMD, cmd);
}
// 写的数据为RAM数据
void oled_write_data(uint8_t data)
{
    oled_i2c_write_byte(OLED_DATA, data);
}
// oled初始化
void oled_init(void)
{
    // 关显示0xAE：关闭显示（进入休眠模式，屏幕无输出）。0xAF：开启显示（正常工作模式）。
    oled_write_command(0xAE);
    // 设置低列地址
    oled_write_command(0x00);
    // 设置高列地址
    oled_write_command(0x10);
    // 设置起始行地址0x40（起始行0）到 0x7F（起始行63）。
    oled_write_command(0x40);
    // 设置页地址0xB0（第0页）到 0xB7（第7页），共8页（对应64行）。
    oled_write_command(0xB0);
    // 设置对比度
    oled_write_command(0x81);
    oled_write_command(0xFF);
    // 设置段重映射（水平翻转）0xA0：列地址0映射到屏幕左侧（默认）。0xA1：列地址0映射到屏幕右侧（水平镜像）。
    oled_write_command(0xA1);
    // 设置公共输出扫描方向（垂直翻转）0xC0：正常方向（COM0到COM63从上到下）。0xC8：反向扫描（COM63到COM0从上到下）。
    oled_write_command(0xC8);
    // A6：关反显，A7：开反显。在不重写显示数据RAM内容的情况下反转显示开/关状态。
    // 0xA6	正常显示模式	逻辑1点亮像素，逻辑0熄灭（默认模式）
    // 0xA7	反转显示模式	逻辑1熄灭像素，逻辑0点亮（反色模式，常用于高亮显示或特定视觉效果）
    oled_write_command(0xA6);
    // 设置多路复用比率
    // 定义COM引脚的有效行数，直接影响OLED屏幕的垂直分辨率。参数值N需设置为实际行数减1（如64行屏幕应配置为0x3F）。
    oled_write_command(0xA8);
    oled_write_command(0x3F);
    // 设置显示偏移模式
    // 范围 0x00~0x3F（对应0~63行偏移）。
    oled_write_command(0xD3);
    oled_write_command(0x00);
    // 设置显示时钟分频比/振荡器频率
    //[7:4]振荡器频率 + [3:0]分频因子，猜想：1：实际频率=基频*（振荡器频率+1）/分频因子；2：实际频率=基频*（1+15%）/分频因子
    oled_write_command(0xD5);
    oled_write_command(0x80);
    // 设置预充电周期的持续时间。间隔以DCLK的个数计。POR是2个DCLKs。
    // 周期持续时间（00-FF）。0xF1：预充电周期的持续时间为1个DCLK，掉电周期的持续时间为15个DCLK。
    oled_write_command(0xD9);
    oled_write_command(0xF1);
    // 设置公用焊盘硬件配置。此命令用于设置公共信号板配置（顺序或替代）以匹配OLED面板硬件布局。
    // 顺序/替代模式设置（02-12）。0x02：顺序模式。0x12：替代模式。
    oled_write_command(0xDA);
    oled_write_command(0x12);
    // 设置VCOM取消选择级别。此命令用于在取消选择阶段设置公共焊盘输出电压电平。
    // VCOM是 OLED 像素在 非选通状态（关闭状态） 的参考电压。该电压值决定了像素关闭时的 残留电流 和 显示对比度。
    // 较高的 VCOM 级别 → 更彻底的像素关闭（减少残影），但可能降低对比度。较低的 VCOM 级别 → 更高对比度，但可能产生轻微漏光。
    oled_write_command(0xDB);
    oled_write_command(0x30);
    // 设置内存地址模式
    //0x00：水平地址模式（逐行填充，地址指针自动右移）。
    //0x01：垂直地址模式（逐列填充，地址指针自动下移）。
    //0x02：页地址模式（按页填充，地址指针在页内右移）。
    oled_write_command(0x20);
    oled_write_command(0x00);
    // 关闭/打开整个显示。0xA4：正常显示。0xA5：整个显示。此命令的优先级高于正常/反向显示命令。
    oled_write_command(0xA4);
    // 设置电荷泵（内部升压电路）0x14：开启电荷泵（必须启用，否则屏幕无显示）。0x10：关闭电荷泵。
    oled_write_command(0x8D);
    oled_write_command(0x14);
    // 开显示
    oled_write_command(0xAF);
}
// 全屏显示
void oled_display_all(void)
{
    uint8_t i, j;
    for (i = 0; i < PAGE_MAX; i++)
    {
        oled_write_command(0xB0 + i);
        oled_write_command(0x00);
        oled_write_command(0x10);
        for (j = 0; j < COL_MAX; j++)
        {
            oled_write_data(0xFF);
        }
    }
}
// 清屏
void oled_clear_all(void)
{
    uint8_t i, j;
    for (i = 0; i < PAGE_MAX; i++)
    {
        oled_write_command(0xB0 + i);
        oled_write_command(0x00);
        oled_write_command(0x10);
        for (j = 0; j < COL_MAX; j++)
        {
            oled_write_data(0x00);
        }
    }
}
// 设置oled显示位置
void oled_set_pos(uint8_t x, uint8_t y)
{
    oled_write_command(0xB0 + y);
    oled_write_command((x & 0x0F) | 0x01);
    oled_write_command(((x & 0xF0) >> 4) | 0x10);
}
// 显示字符
void oled_show_char(uint8_t x, uint8_t y, uint8_t ch, uint8_t size)
{
    uint8_t c_index, i;
    c_index = ch - ' ';
    if (size == 16)
    {
        if (x > COL_MAX - 8)
        {
            x = 0;
            y += 2;
        }
        oled_set_pos(x, y);
        for (i = 0; i < 8; i++)
        {
            oled_write_data(F8X16[c_index * 16 + i]);
        }
        oled_set_pos(x, y + 1);
        for (i = 0; i < 8; i++)
        {
            oled_write_data(F8X16[c_index * 16 + i + 8]);
        }
    }
    else if (size == 8)
    {
        if (x > COL_MAX - 6)
        {
            x = 0;
            y += 1;
        }
        oled_set_pos(x, y);
        for (i = 0; i < 6; i++)
        {
            oled_write_data(F6x8[c_index][i]);
        }
    }
    else
    {
        oled_show_error(x, y, (uint8_t *)"Error!");
    }
}
// 显示字符串
void oled_show_string(uint8_t x, uint8_t y, uint8_t *str, uint8_t size)
{
    uint8_t i = 0;
    while (str[i] != '\0')
    {
        oled_show_char(x, y, str[i], size);
        if (size == 16)
        {
            x += 8;
            if (x > COL_MAX - 8)
            {
                x = 0;
                y += 2;
            }
        }
        else if (size == 8)
        {
            x += 6;
            if (x > COL_MAX - 6)
            {
                x = 0;
                y += 1;
            }
        }
        i++;
    }
}
// 显示"error"
void oled_show_error(uint8_t x, uint8_t y, uint8_t *err)
{
    oled_show_string(x, y, err, 16);
}
